using System;
using System.Collections.Generic;
using System.Text;
using System.Net;

namespace CodePlex.Resourceful
{


    // used by the base Client to workaround known issues with the HttpWebRequest object
    // currently:
    //    1. allowing uris with multiple dots (..) or multiple slashes (//) or backslashes
    //    2. manually setting the host header
    internal class UriHacker : IDisposable
    {
        private readonly Uri _uri;
        private readonly UriParser _originalParser;
        private readonly string _originalHost;

        public UriHacker(HttpWebRequest request, string hostHeader)
        {
            if (!HackApplies(request, hostHeader))
            {
                return;
            }

            // apply hack
            if (EnvironmentInfo.IsMono)
            {
                _uri = Utils.GetInstanceField(request, "actualUri") as Uri;
                _originalHost = Utils.GetInstanceField(_uri, "host") as string;
                Utils.SetInstanceField(_uri, "host", hostHeader);
            }
            else
            {
                _uri = Utils.GetInstanceField(request, "_Uri") as Uri;
                _originalParser = Utils.GetInstanceField(_uri, "m_Syntax") as UriParser;
                UriParser newParser = new WrappedUriParser(request, _originalParser, hostHeader);
                Utils.SetInstanceField(_uri, "m_Syntax", newParser);
            }

        }

        private static bool HackApplies(HttpWebRequest request,string hostHeader)
        {
            if (EnvironmentInfo.IsSilverlight)
            {
                // no known equivalent yet...
                return false;
            }
           
            if (hostHeader != null)
            {
                return true;
            }

            // only host header hack supported on mono right now...
            if (EnvironmentInfo.IsMono)
            {
                return false;
            }

            string originalString = request.RequestUri.OriginalString;
            int q = originalString.IndexOf('?');
            if (q > -1)
            {
                originalString = originalString.Substring(0, q);
            }

            if (originalString.Contains("%5c"))     // utf8 backslash
            {
                return true;
            }

            if (originalString.Contains("%2f"))     // utf8 slash
            {
                return true;
            }

         
            if (originalString.Contains(".."))
            {
                return true;
            }

            if (originalString.Substring(request.RequestUri.GetComponents(UriComponents.SchemeAndServer,UriFormat.UriEscaped).Length).Contains("."))
            {
                return true;
            }

            return false;
        }

        public void Dispose()
        {
            if (_uri != null)
            {
                // reset hack
                if (EnvironmentInfo.IsMono)
                {
                    Utils.SetInstanceField(_uri, "host", _originalHost);
                }
                else
                {
                    Utils.SetInstanceField(_uri, "m_Syntax", _originalParser);
                }
            }
        }





        private class WrappedUriParser : UriParser
        {
            private readonly HttpWebRequest _request;
            private readonly UriParser _other;
            private readonly string _hostHeader;
          
            public WrappedUriParser(HttpWebRequest request, UriParser other, string hostHeader)
            {
                _request = request;
                _other = other;
                _hostHeader = hostHeader;
               

                object scheme = Utils.GetInstanceField(_other, "m_Scheme");
                Utils.SetInstanceField(this, "m_Scheme", scheme);

                object port = Utils.GetInstanceField(_other, "m_Port");
                Utils.SetInstanceField(this, "m_Port", port);

            }

            protected override string GetComponents(Uri uri, UriComponents components, UriFormat format)
            {
                if (_hostHeader != null )
                {
                    // host header hack for all versions of ms fx
                    object m_info = Utils.GetInstanceField(uri, "m_Info");
                    Utils.SetInstanceField(m_info, "Host", _hostHeader);
                }

                if (components == UriComponents.PathAndQuery)
                {
                    string s = base.GetComponents(uri, UriComponents.SchemeAndServer, format);
                    s =  uri.OriginalString.Substring(s.Length);
                    return s;
                }
                else
                {
                    return base.GetComponents(uri, components, format);
                }
            }

        }
    }

}
